package homeworks.cjt2010.homework1;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class AirportManagerSystem implements AirportManager {
	// --- Static fields -------------------------------------------------------
	private static final int MILLIS_IN_HOUR = 3600000;

	// --- Instance Variables --------------------------------------------------
	Airport systemAirport = null;
	Map<Flight, Collection<Ticket>> flightTickets = null;
	Map<Ticket, Collection<Object>> ticketBaggages = null;

	Map<Flight, FlightStatus> flightFlightStatus = null;

	int flightStatusCount = FlightStatus.values().length;
	/**
	 * Simulates a list of "buckets"(using set interface). There is a bucket for
	 * each <code>flight status</code> where we put the <code>flight</code>
	 * according to its status.
	 * 
	 * <br/>
	 * For the current purpose of the task it is unnecessary because for now we
	 * won't use FlightStatus.CANCELED and FlightStatus.DELAYED constants. <br/>
	 * But in the future(next homework) when they are going to be added, this
	 * collection will give us more quick answer if the flight is correct input
	 * argument for methods
	 * {@link AirportManagerSystem#bookTicket(Flight, FlightClass, Passenger...)
	 * bookTicket()} and
	 * {@link AirportManagerSystem#rescheduleFlight(Ticket, Flight)
	 * rescheduleFlight()}
	 */
	List<Set<Flight>> FlightStatusFlightPairBuckets = new ArrayList<Set<Flight>>(
			flightStatusCount);

	// --- Constructors --------------------------------------------------------
	public static AirportManager createManagerSystem(Airport airport) {
		if (airport == null) {
			throw new IllegalArgumentException(
					"Incorrect input airport-parameter "
							+ "during creation of AirportManagerSystem");
		}
		return new AirportManagerSystem(airport);
	}

	private AirportManagerSystem(Airport airport) {
		this.systemAirport = airport;
		flightTickets = new HashMap<Flight, Collection<Ticket>>();
		ticketBaggages = new HashMap<Ticket, Collection<Object>>();
	}

	public AirportManagerSystem(Airport airport, Set<Airport> airports,
			Map<Flight, Collection<Ticket>> flightTickets,
			Map<Ticket, Collection<Object>> ticketBaggages) {
		this.systemAirport = airport;
		this.flightTickets = flightTickets;
		this.ticketBaggages = ticketBaggages;
	}

	// --- Getters and Setters -------------------------------------------------

	public Map<Flight, Collection<Ticket>> getFlightTickets() {
		return flightTickets;
	}

	public void setFlightTickets(Map<Flight, Collection<Ticket>> flightTickets) {
		this.flightTickets = flightTickets;
	}

	public Map<Flight, FlightStatus> getFlightFlightStatus() {
		return flightFlightStatus;
	}

	public void setFlightFlightStatus(
			Map<Flight, FlightStatus> flightFlightStatus) {
		this.flightFlightStatus = flightFlightStatus;
	}

	// --- Methods -------------------------------------------------------------

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder(100);
		sb.append("System airport: " + systemAirport);
		sb.append(flightTickets);
		sb.append(ticketBaggages);

		return sb.toString();
	}

	@Override
	public Collection<Flight> getFlights(Airport to) {
		// check method-parameter
		if (to == null) {
			throw new IllegalArgumentException("Null-pointed input argument "
					+ "in getFlights() method");
		}
		if (!(to instanceof Airport)) {
			throw new IllegalArgumentException("The input argument "
					+ "in getFlights() method is not from enum-type Airport.");
		}

		Collection<Flight> result = new HashSet<Flight>();
		Set<Flight> flights = flightTickets.keySet();

		for (Flight flight : flights) {
			boolean checkSourceAirport = flight.getSourceAirport().equals(
					systemAirport);
			if (checkSourceAirport && flight.getDestinationAirport().equals(to)) {
				result.add(flight);
			}
		}

		return result.isEmpty() ? null : result;
	}

	@Override
	public Collection<Ticket> bookTicket(Flight flight,
			FlightClass flightClass, Passenger... person)
			throws IllegalArgumentException {

		// check the flight argument
		if (flight == null || (!(flight instanceof Flight))) {
			throw new IllegalArgumentException(
					"Incorrect input flight in bookTicket() method");
		}
		// check if the flight is in the system
		if (!(flightTickets.containsKey(flight))) {
			throw new IllegalArgumentException("There is no such flight"
					+ " in the airport system");
		}

		// check FlightStatus
		// TO DO - to be changed after answer of lectures in forum
		FlightStatus fs = flightFlightStatus.get(flight);
		if (fs.equals(FlightStatus.DEPARTED)
				|| fs.equals(FlightStatus.CANCELED)) {
			throw new IllegalArgumentException("The wanted flight"
					+ " has departed or canceled. ");
		}

		// check FlightClass
		if (flightClass == null || (!(flightClass instanceof FlightClass))) {
			throw new IllegalArgumentException(
					"Incorrect flightClass argument.");
		}

		// calculate price
		if (person.length == 0) {
			throw new IllegalArgumentException("Incorrect person argument.");
		}
		if (person.length == 1) {
			// calculate discount
			PersonalDiscount pDiscount = PersonalDiscount.NULL_LEVEL;
			for (PersonalDiscount discount : PersonalDiscount.values()) {
				if (person[0].getMiles() <= discount.getUpperBound()) {
					pDiscount = discount;
					// JUNIT TEST
					// System.out.println(discount);
					break;

				}
			}
			double price = (flightClass.getPrice())
					* (1 - pDiscount.getPercent());
			Ticket ticket = new Ticket(flight, price, flightClass, -1,
					person[0]);
			// add the new ticket to flightTickets Map
			Collection<Ticket> collectionTickets = flightTickets.get(flight);
			collectionTickets.add(ticket);

			return Collections.singleton(ticket);
		}

		// person.lenght > 1
		return bookGroupTickets(flight, flightClass, person);
	}

	private Collection<Ticket> bookGroupTickets(Flight flight,
			FlightClass flightClass, Passenger... person) {
		// person.lenght > 1

		Collection<Ticket> newTickets = new HashSet<Ticket>();
		GroupDiscount gDiscount = GroupDiscount.NULL_LEVEL;
		for (GroupDiscount discount : GroupDiscount.values()) {
			if (person.length <= discount.getUpperBound()) {
				gDiscount = discount;
				// JUNIT TEST
				// System.out.println("Group: " + gDiscount);
				break;
			}
		}
		// calculate Frequent flyer discount(<5 persons) OR Group discount(>=5
		// persons) according to passengers number
		if (gDiscount.equals(GroupDiscount.NULL_LEVEL)) { // pass.lenght <5
			PersonalDiscount pDiscount = PersonalDiscount.NULL_LEVEL;
			for (int iter = 0; iter < person.length; iter++) {
				pDiscount = PersonalDiscount.NULL_LEVEL;

				for (PersonalDiscount discount : PersonalDiscount.values()) {
					if (person[iter].getMiles() <= discount.getUpperBound()) {
						pDiscount = discount;
						// JUNIT TEST
						// System.out.println("Person: " + discount);
						break;
					}
				}

				double price = (flightClass.getPrice())
						* (1 - pDiscount.getPercent());
				Ticket ticket = new Ticket(flight, price, flightClass, -1,
						person[iter]);
				// add the new ticket the collection<Ticket> which the method
				// returns
				newTickets.add(ticket);
			}

		} else { // pass.lenght >= 5
			double price = (flightClass.getPrice())
					* (1 - gDiscount.getPercent());
			for (Passenger pass : person) {
				Ticket ticket = new Ticket(flight, price, flightClass, -1, pass);
				newTickets.add(ticket);
			}
		}

		// add the new tickets to flightTickets Map
		Collection<Ticket> collectionTickets = flightTickets.get(flight);
		collectionTickets.addAll(newTickets);

		return newTickets;
	}

	@Override
	public String flightInfo(Flight flight) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public FlightStatus getFlightStatus(Flight flight) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean registerFlights(Flight... flights) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean unregisterFlight(Flight flight) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Collection<Ticket> getPurchasedTickets(Flight flight) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Ticket rescheduleFlight(Ticket oldTicket, Flight newFlight)
			throws NoSuchFlightException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Collection<Flight> getDepartingFlights(Date from, Date to) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Collection<Flight> getArrivingFlights(Date from, Date to) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void checkInBaggage(Ticket ticket, Object... baggage) {
		// TODO Auto-generated method stub

	}

	@Override
	public Object[] getBaggage(Ticket ticket) throws IllegalArgumentException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean store(File f) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean load(File f) {
		// TODO Auto-generated method stub
		return false;
	}

}
